#include <time.h>
#include <winsock2.h>
#include "MySQL/mysql.h"
#include <ws2tcpip.h>
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"

#include "CNetwork.h"
#include "CGlobal.h"
#include "CConsole.h"
#include "CZlib.h"
#include "CUtil.h"
#include "CDb.h"
#include "CBlowfish.h"
#include "CPacket.h"
#include "mainloop.h"
#include "CBasicPacket.h"
#include "CSearchRequestPacket.h"
#include "CSearchResponsePacket.h"
#include "CChar.h"
#include "CParty.h"
#include "CLinkshell.h"
#include "CLinkshellItem.h"

#define ATTEMPT_LOGIN			0x10
#define ATTEMPT_CREATE_ACOUNT	0x20

#define SUCCESS_LOGIN			0x01
#define ERROR_LOGIN				0x02
#define SUCCESS_CREATE_ACCOUNT  0x03
#define ERROR_CREATE_ACCOUNT	0x04
#define ERROR_DISABLED			0x05
#define ERROR_INACTIVE			0x06
#define ERROR_BANNED			0x07

#define MAX_SEARCH_ENTRIES		100

CNetwork::CNetwork(void)
{
}

CNetwork::~CNetwork(void)
{
}


int CNetwork::initCommunication(SOCKET * ListenSocket, int protocol , char * port){
	struct addrinfo hints, * result = NULL;
	int iResult;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
	hints.ai_socktype = (protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
    hints.ai_protocol = protocol;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, port, &hints, &result);
    if ( iResult != 0 ) {
		CConsole::outErr("getaddrinfo failed: %d\n, %s", iResult, port);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for connecting to server
    *ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (*ListenSocket == INVALID_SOCKET) {
      	freeaddrinfo(result);
       	CConsole::outErr("socket failed: %ld\n", WSAGetLastError());
       	WSACleanup();
        return 1;
    }

    // Setup the TCP listening socket
    iResult = bind( *ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
       	freeaddrinfo(result);
       	closesocket(*ListenSocket);
       	CConsole::outErr("bind failed: %d %s", WSAGetLastError(),port);
       	WSACleanup();
        return 1;
    }
    freeaddrinfo(result);

	if(protocol == IPPROTO_TCP){
		iResult = listen(*ListenSocket, SOMAXCONN);
	    if (iResult == SOCKET_ERROR) {
	       	CConsole::outErr("listen failed: %d\n", WSAGetLastError());
	       	WSACleanup();
	        return 1;
	    }
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Server Threads
//////////////////////////////////////////////////////////////////////////////////////////

#define DEFAULT_BUFLEN 8192

DWORD WINAPI UDPServ(LPVOID lpParam)
{
    SOCKET ListenSocket = INVALID_SOCKET;
	struct UDPdata * UDPcomm;
	UDPcomm = (UDPdata *)lpParam;
	char buf[255];

	CConsole::outHr();
	UDPcomm->status = 50;
	mysql_thread_init();
	CConsole::outTime("Initializing Compression... ");
	CZlib::init();
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");
	CConsole::outHr();
	CConsole::outTime("Starting Project Axium World Server... ");
	
	if(CNetwork::initCommunication(&ListenSocket, IPPROTO_UDP, CUtil::getINIstring("configure.ini", "Server", "UDPPORT", buf))){
		CConsole::outErr("Failed");
		mysql_thread_end();
		return 1;
	}
	
	CConsole::outTime(FOREGROUND_GREEN, "Running!");

	// could be used to recompile without dc'ing in the past.
	//CDb::loadCharacters();
	mainloop(&ListenSocket, CZlib::getCompressTable(), CZlib::getDecompressTable(), UDPcomm);

    // cleanup
    closesocket(ListenSocket);
	mysql_thread_end();

    return 0;
}


DWORD WINAPI LobbyServ(LPVOID lpParam)
{
	SOCKET ListenSocket = INVALID_SOCKET;
	SOCKET ListenSocket2 = INVALID_SOCKET;
	SOCKET ClientSocket = INVALID_SOCKET;

	struct sockaddr_in ip;
	int size;

	//Listen for the client to make a connection
	if(CNetwork::initCommunication(&ListenSocket, IPPROTO_TCP, "54230")){
		CConsole::outErr("Failed");
		return 1;
	}

	//Listen for FFXI to connect to us
	if(CNetwork::initCommunication(&ListenSocket2, IPPROTO_TCP, "54001")){
		CConsole::outErr("Failed");
		return 1;
	}

	//Wait for FFXI to connect to us
	CreateThread(0,0,LobbyComm,&ListenSocket2,0,0);

	while(1) {
		size = sizeof(ip);

		//Listens for our client to connect
		ClientSocket = accept(ListenSocket, (struct sockaddr *)&ip, &size);
		CConsole::outDebOnly("Accepting connection on ListenSocket: Address: '%s:%u'",inet_ntoa(ip.sin_addr),htons(ip.sin_port));
		if (ClientSocket == INVALID_SOCKET) {
	       	closesocket(ListenSocket);
	      	CConsole::outErr("accept failed: %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}

		// Send account ID request to client
		unsigned int acctID = 0;
		int timeout = 0;
		char buf[40];
		memset(buf, 0, sizeof(buf));
		while(((unsigned char)buf[0] != 0xa1) && timeout < 20){
			buf[0]= 0x01;
			send(ClientSocket, buf, 5 ,0 );
			Sleep(700);
			recv(ClientSocket, buf, 40 ,0);
			timeout++;
		}

		if(timeout <= 20) {
			memcpy(&acctID, buf+1,4);
			CConsole::outDebOnly("Account ID: %i",acctID);

			CGlobal::lockLobbyConList();
			struct connections	clientConn;
			memset(&clientConn,0,sizeof(struct connections));
			clientConn.lobbySocket = ClientSocket;
			clientConn.clientIP = ip.sin_addr.S_un.S_addr;
			clientConn.acctID = acctID;
			clientConn.time = (unsigned int)time(NULL);
			CGlobal::lobbyConList.push_back(clientConn);
			CGlobal::unlockLobbyConList();
		} else {
			shutdown(ClientSocket, SD_SEND);
			closesocket(ClientSocket);
			CConsole::outDebOnly("Account ID request timed out.  Closing connection.");
		}
	}
	mysql_thread_end();

}


DWORD WINAPI LobbyComm(LPVOID lpParam){

	SOCKET ClientSocket = INVALID_SOCKET;
	SOCKET ListenSocket2 = INVALID_SOCKET;
	memcpy(&ListenSocket2,lpParam,sizeof(SOCKET));
	struct sockaddr_in ip;
	int size;

	while(1) 
	{
		size = sizeof(ip);
		//Listen for FFXI's connection
		ClientSocket = accept(ListenSocket2, (struct sockaddr *)&ip, &size);
		CConsole::outDebOnly("Acception connection on ListenSocket2: Address: '%s:%u'",inet_ntoa(ip.sin_addr),htons(ip.sin_port));
		if (ClientSocket == INVALID_SOCKET) {
	       	closesocket(ClientSocket);
	      	CConsole::outErr("accept failed: %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}

		bool conFound = false;

		struct connections* currCon = NULL;
		struct connections* foundCon = NULL;
		unsigned int currConIndex;
		unsigned int timeAt;
		char tryNum = 0;
		while((!conFound) && (tryNum<3))
		{
			tryNum++;
			foundCon = NULL;
			timeAt = 0;
			Sleep(200);
			CGlobal::lockLobbyConList();
			currConIndex = (unsigned int)CGlobal::lobbyConList.size();
			CConsole::outDebOnly("Lobby con list contains %d entries",currConIndex);

			//Search the con list from the back
			while (currConIndex > 0)
			{
				currConIndex--;
				currCon = &CGlobal::lobbyConList.at(currConIndex);

				//Check if this connection belongs to the current ip
				//If we found another connection from the same ip: 
				//Check if the current connection info is newer
				if ( (currCon->clientIP == ip.sin_addr.S_un.S_addr) &&
					 (currCon->polSocket == 0) &&
					 (timeAt < currCon->time))
				{
					timeAt = currCon->time;
					if (foundCon!=NULL)
					{
						CGlobal::lobbyConList.push_back(*foundCon);
						delete foundCon;
					}
					foundCon = new struct connections(*currCon);
					CGlobal::lobbyConList.erase(CGlobal::lobbyConList.begin()+currConIndex);
				}
				else
				{
					//check if the connection has timed out (will happen if the client used shutdown or disconnected without noticing the server)
					CConsole::outDeb("Time since creation for entry %d : %d",currConIndex,( ((unsigned int)time(NULL)) - currCon->time ));
					if ( ( ((unsigned int)time(NULL)) - currCon->time ) > 30) //Entry older than 5 minutes
					{
						CConsole::outDebOnly("Removing an old connection!");
						CGlobal::lobbyConList.erase(CGlobal::lobbyConList.begin()+currConIndex);
					}
				}
			}
			if (foundCon!=NULL)
			{
				CConsole::outTime("Found entry in lobby connection list.");
				foundCon->polSocket = ClientSocket;
				CreateThread(0,0,PolLobbyComm,foundCon,0,0);
				conFound=true;
			}
			CGlobal::unlockLobbyConList();
			CConsole::outDebOnly("Unlocked con list!");
		}
	}
	mysql_thread_end();
	return 0;
}

DWORD WINAPI AccServ(LPVOID lpParam){
	SOCKET ListenSocket = INVALID_SOCKET;
	SOCKET ClientSocket = INVALID_SOCKET;
	struct connections clientConn;
	struct sockaddr_in ip;
	int size;

	if(CNetwork::initCommunication(&ListenSocket, IPPROTO_TCP, "54231")){
		CConsole::outErr("Failed");
		return 1;
	}

	while(1) {
		size = sizeof(ip);
		clientConn.outSocket = 0;
		clientConn.acctSocket = accept(ListenSocket, (struct sockaddr *)&ip, &size);
		if (clientConn.acctSocket == INVALID_SOCKET) {
	       	closesocket(ListenSocket);
	      	CConsole::outErr("accept failed: %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
		clientConn.clientIP = ip.sin_addr.S_un.S_addr;

		mysql_thread_init();
		CreateThread(0,0,AccComm,&clientConn,0,0);
		Sleep(200);
	}
}

DWORD WINAPI AccComm(LPVOID lpParam){
	struct connections clientConn;
	memcpy(&clientConn, lpParam, sizeof(connections));
	char accData[33];
	char username[16];
	char password[16];
	char sendbuf[16];
	memset(accData,0,33);
	memset(sendbuf,0,16);
	MYSQL_RES * res;
	MYSQL_ROW row;
	int size = 0, count;
	char query[8192];

	MYSQL* mysqlConn;
	
	int received=recv(clientConn.acctSocket,accData,33,0);
	if (received<0)
	{
		CConsole::outErr("Receive of account packet failed. Error: %i",WSAGetLastError());
		return 0;
	}
	else if (received<33)
	{
		CConsole::outErr("Received an account packet with a size<33. Size %i",received);
		return 0;
	}

	memcpy(username,accData,16);
	memcpy(password,accData+16,16);

	CConsole::outTime("%s",username);
	CConsole::outTime("%s",password);
	switch(accData[32]) {
		case ATTEMPT_LOGIN:
		
			sprintf(query, "SELECT id, status FROM accounts WHERE aident = '%s' AND pass = '%s'",
				username,password);
		
			count = (int) strlen(query);

			mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, count);

			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			if(!res)
			{
				sendbuf[0] = ERROR_LOGIN;
			}
			else if(!mysql_num_rows(res)){
				mysql_free_result(res);
				sendbuf[0] = ERROR_LOGIN;
			}
			else {

				row = mysql_fetch_row(res);
				int acc = 0;

				// Check if the user is banned, disables, or Inactive.
				int status = atoi(row[1]);
				if(status == 1){
					sendbuf[0] = ERROR_INACTIVE;
				} else if (status == 2){
					sendbuf[0] = ERROR_DISABLED;
				} else if (status == 3){
					sendbuf[0] = ERROR_BANNED;
				} else {
					sendbuf[0] = SUCCESS_LOGIN;
					acc = atoi(row[0]);
				}

				CConsole::outTime("LOGIN-AccountID: %i",acc);
				memcpy(sendbuf+1,(char*)&acc,4);
				mysql_free_result(res);

			}
			
			send(clientConn.acctSocket,sendbuf,16,0);
			break;
		case ATTEMPT_CREATE_ACOUNT:
			sendbuf[0] = 0x02;

			sprintf(query, "SELECT max(id) FROM accounts");
			count = (int) strlen(query);
			mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, count);

			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn
			if(!res)
			{
				sendbuf[0] = ERROR_CREATE_ACCOUNT; //Query failed
			}
			else if(!mysql_num_rows(res)){
				mysql_free_result(res);
				sendbuf[0] = ERROR_CREATE_ACCOUNT; //No Results
			}
			else {

				row = mysql_fetch_row(res);

				int acc = 999; 
				if (NULL != row[0]){
					acc = atoi(row[0]);
				}
				acc++; // Add 1 to get the new number
				mysql_free_result(res);
				sprintf(query, "SELECT id FROM accounts WHERE aident ='%s'",username);
				count = (int) strlen(query);

				mysqlConn=CDb::getConn(); //get conn
				mysql_real_query(mysqlConn, query, count);

				res = mysql_store_result(mysqlConn);
				CDb::pushConn(mysqlConn); //release conn

				if(!res)
				{
					sendbuf[0] = ERROR_CREATE_ACCOUNT; //Query failed
				}
				else if(mysql_num_rows(res))
				{
					mysql_free_result(res);
					sendbuf[0] = ERROR_CREATE_ACCOUNT; //There are characters
				}
				else {
					//Success, new account can be made
					sprintf(query,"insert into `accounts` (`id`, `aident`, `pass`, `privs`, `comment`, `status`, `created`) values(%d,'%s','%s','5','NEW ACCOUNT','0',CURRENT_TIMESTAMP)",acc,username,password);
					count = (int) strlen(query);

					mysqlConn=CDb::getConn(); //get conn
					mysql_real_query(mysqlConn, query, count);
					CDb::pushConn(mysqlConn); //release conn

					sendbuf[0] = SUCCESS_CREATE_ACCOUNT; //Tell the client it was successful
				}
			}

			CConsole::outTime("new account attempt");
			
			send(clientConn.acctSocket,sendbuf,16,0); //Send whatever happened to the client
			break;
	}


	mysql_thread_end();
	
	CConsole::outTime("Client requesting authentication");
	return 1;
}

DWORD WINAPI PolLobbyComm(LPVOID lpParam)
{
	struct connections* clientConn = (struct connections*)lpParam;

	//struct RecvKey r = *r1;
	struct addrinfo *result = NULL, *ptr = NULL;
	int iResult;

	int socksize = sizeof(struct sockaddr_in);
	char recvbuf[DEFAULT_BUFLEN], buf[DEFAULT_BUFLEN], output[DEFAULT_BUFLEN];
	long size = 0;
	SOCKET ConnectSocket = INVALID_SOCKET;
	SOCKET ListenSocket = INVALID_SOCKET;

	buf[0] = 0x01;

	bool running=true;
	while(running)
	{
		// Recives packets
		memset(recvbuf, 0, sizeof(recvbuf));
		iResult = recv(clientConn->polSocket, recvbuf, DEFAULT_BUFLEN, 0);
		if (iResult<=0)
		{
			running=false;
			if ((WSAGetLastError()==WSAENOTSOCK) || 
				(WSAGetLastError()==WSAECONNRESET))
				CConsole::outErr("Closing LobbyComm Thread, Socket dead.");
			else
				CConsole::outErr("Lobby packet receive failed. Skipping the packet. Terminating socket connection!(WSAError: %d)",WSAGetLastError());
			continue;
		}

		if (iResult<4)
		{
			CConsole::outErr("Received a lobby packet with size<4. Size: %i. Skipping the packet.",iResult);
			continue;
		}

		size = CPacket::genLobbyPacket(output, recvbuf, clientConn);

		if(size == -1) 
		{
			CConsole::outErr("GenLobbyPacket returned -1!");
			delete clientConn;
			return 0;
		}
		if(size == 0)
			continue;

		// Send response to client
		iResult = send(clientConn->polSocket, output, size,0);
		if( size == 72 || iResult == SOCKET_ERROR || size == -1 ) {
			shutdown(clientConn->polSocket, SD_SEND);
			closesocket(clientConn->polSocket);
			CConsole::outTime("Lobby connection done, disconnecting!");
			running=false;
		}
	}
	//delete the connection info, this is either unused (client crash etc) or stored with the char the client just logged in
	delete clientConn;
	return 0;
}

struct searchCommInfo
{
	SOCKET* socket;
	unsigned int ip;
	u_short port;
};

DWORD WINAPI SearchServ(LPVOID lpParam)
{
    SOCKET ListenSocket = INVALID_SOCKET;
	SOCKET ClientSocket = INVALID_SOCKET;

	struct sockaddr_in ip;
	int size;


	CConsole::outHr();

	mysql_thread_init();

	CConsole::outTime("Starting Project Axium  Search Server... ");
	
	if(CNetwork::initCommunication(&ListenSocket, IPPROTO_TCP, "54002")){
		CConsole::outErr("Failed");
		mysql_thread_end();
		return 1;
	}
	
	CConsole::outTime(FOREGROUND_GREEN, "Running!");
	CConsole::outHr();

	while(1) 
	{
		size = sizeof(ip);
		ClientSocket = accept(ListenSocket, (struct sockaddr *)&ip, &size);
		if (ClientSocket == INVALID_SOCKET) {
	       	closesocket(ListenSocket);
	      	CConsole::outErr("Accept failed: %d\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}
		mysql_thread_init();
		struct searchCommInfo seaCommInfo;
		seaCommInfo.socket=&ClientSocket;
		seaCommInfo.ip=ip.sin_addr.S_un.S_addr;
		seaCommInfo.port=ip.sin_port;
		CreateThread(0,0,SearchComm,&seaCommInfo,0,0);;
		Sleep(200);
	}


    // cleanup
    closesocket(ListenSocket);
	mysql_thread_end();

    return 0;
}

DWORD WINAPI SearchComm(LPVOID lpParam)
{
	struct searchCommInfo seaCommInfo(*((struct searchCommInfo*)lpParam));
	unsigned int clientIP=seaCommInfo.ip;
	unsigned short clientPort=seaCommInfo.port;
	CConsole::outDebOnly("Search from client with port: %u",htons(seaCommInfo.port));
	SOCKET s = *((SOCKET*) seaCommInfo.socket);

	CChar * tempChar;
	CParty * searchParty = NULL; //Set the searchParty to NULL to be safe

	CSearchRequestPacket* searchRequestP=new CSearchRequestPacket();
	searchRequestP->receiveFromSocket(&s);

	char key[24];
	memcpy(&key,searchRequestP->getKey(),24);

	struct seaPacket packetData(*searchRequestP->getPacketData());

	if (packetData.getActiveLS)
	{
		CChar* myChar=NULL;
		//Do a db query for the char
		CGlobal::waitCharList(); //Wait for the char list to become free.
		for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) 
		{
			tempChar = CGlobal::getCCharAt(k);
			if (tempChar != NULL) {
				if ( (clientIP==tempChar->clientConn.clientIP) &&
					 (tempChar->getLinkshell()!=NULL))
				{
					myChar=tempChar;
					CConsole::outDebOnly("Searching Char: %s, may be wrong if multiple chars work from this ip!",tempChar->getName());
					break;
				}
			}
		}
		CGlobal::exitCharList();
		if (myChar!=NULL)
		{
			packetData.ls = myChar->getLinkshell();
			if (packetData.ls != NULL)
				packetData.lsId=packetData.ls->getLsId();
			CLinkshellItem * lsItem;
			lsItem = myChar->getLinkshellItem();
			if(lsItem != NULL)
				CConsole::outDebOnly("LinkshellItem: %.4X",lsItem->getId());
		}
	}

	if ((packetData.ls == NULL) && (packetData.lsId!=0))
	{
		packetData.ls = CLinkshell::getLinkshellById(packetData.lsId);
	}

	//Check if this is a party search
	if (packetData.partyId != 0) 
	{
		//Get the character by the targId
		tempChar = CChar::getCChar(0,"",packetData.partyId);
		if (tempChar != NULL) 
		{
			searchParty = tempChar->getParty(); //Define the searchParty
		}
	}
	//searchRequestP->logPacket();

	//delete the request packet object
	delete searchRequestP;

	//set our own key1
	memcpy(packetData.key1,"aXium!",4);

	//copy the keys
	memcpy(key+0x10,packetData.key1,4);
	memcpy(key+0x14,packetData.key2,4);

	struct seaResponseEntry* newEntry=NULL;

	std::vector<struct seaResponseEntry*> resultList;

	T2List<unsigned int,CChar*>* searchList = CGlobal::getCCharList();
	if (packetData.ls!=NULL)
		searchList = packetData.ls->getOnlineMemberList();

	searchList->lock();
	for(unsigned int k = 0; k < searchList->getSize(); k++) 
	{
		tempChar = searchList->at(k);

		CConsole::outDebOnly("Search Char (%d/%d): %s",k+1,searchList->getSize(),tempChar->m_name);

		if (packetData.nameLen>0)
		{
			//charname is shorter than searched -> not in result
			if (strlen(tempChar->m_name)<packetData.nameLen)
				continue;

			char tempName[16];
			strncpy(tempName,tempChar->m_name,packetData.nameLen);
			tempName[packetData.nameLen]='\0';

			//charname does not start with the supplied characters -> not in result
			//atm this comparision is case insensitive
			if ((_stricmp(packetData.name,tempName))!=0)
			{
				continue;
			}
		}

		if (packetData.areaCount>0)
		{
			bool inAreas=false;

			for( int area=0;area<packetData.areaCount;area++)
			{
				// Searching an area will return a char in Moghouse entered from search zone.
				if (tempChar->getDisplayZone()==packetData.areas[area])
					inAreas=true;
			}

			if (inAreas==false)
				continue;
		}

		if (packetData.job>0)
		{
			if ((packetData.job!=tempChar->m_jobMain) && (packetData.job!=tempChar->m_jobSub))
				continue;
		}

		if (packetData.race!=255)
		{
			//Realrace  -> Search Race
			//0x01,0x02 -> 0x00 //Hume
			//0x03,0x04 -> 0x01 //Elvaan
			//0x05,0x06 -> 0x02 //TaruTaru
			//0x07		-> 0x03 //Mithra
			//0x08		-> 0x04 //Galka
			unsigned char modRace;

			if (tempChar->l.race<0x08)
				modRace=((tempChar->l.race)-1)>>1;
			else
				modRace=(tempChar->l.race)>>1;

			if (modRace!=packetData.race)
				continue;
		}

		if (packetData.country!=255)
		{
			if (tempChar->st.n.home!=packetData.country)
				continue;
		}

		if (packetData.toRank>0)
		{
			unsigned char rank=tempChar->st.n.rank[tempChar->st.n.home];
			if ((rank<packetData.fromRank) || (rank>packetData.toRank))
				continue;
		}

		if (packetData.toLvl>0)
		{
			unsigned char level=tempChar->m_lvlMain;
			if ((level<packetData.fromLvl) || (level>packetData.toLvl))
				continue;
		}

		if (packetData.flags1!=0x0000)
		{
			CConsole::outDebOnly("Flags: 0x%.4X",packetData.flags1);
			CConsole::outDebOnly("Char Flags: 0x%.8X",tempChar->h.flags);

			if ((packetData.flags1&0x0100)==0x0100) // away - BUGGY
			{
				if ((tempChar->h.flags&0x00001000)!=0x00001000)
				{
					CConsole::outDebOnly("Char not away!");
					continue; //not away;
				}
			}
			if ((packetData.flags1&0x8020)==0x8020) // inv
			{
				if ((tempChar->h.flags&0x00000010)!=0x00000010)
				{
					CConsole::outDebOnly("Char not looking for party!");
					continue; //not inv;
				}
			}
		}

		// Party Search
		if ( (packetData.partyId > 0) && (searchParty != NULL) )
		{
			// Is the character in a party?
			if ( (tempChar->inParty > 0) && (tempChar->getParty() != NULL) ) 
			{
				//Yes, now check to see if both the search party and the temp char are in an alliance
				if ( (searchParty->inAlliance > 0) && (searchParty->getAlliance() != NULL) && (tempChar->getParty()->inAlliance > 0) && (tempChar->getParty()->getAlliance() != NULL) ) 
				{
					if (tempChar->getParty()->getAlliance()->getMainParty() != searchParty->getAlliance()->getMainParty())
					{
						continue; //not in the same alliance
					}
				} 
				else if (searchParty != tempChar->getParty()) //not in an alliance
				{
					continue; //Not in the same party
				}
			} 
			else 
			{
				continue; //the character is not even in a party
			}
		} else if ( (packetData.partyId > 0) && (searchParty == NULL) )
		{
			continue;
		}

		//Char is in search result: create entry and add to list
		CConsole::outDebOnly("Adding Char (%d/%d) to result: %s",k+1,searchList->getSize(),tempChar->m_name);

		newEntry=new seaResponseEntry();
		memset(newEntry,0,sizeof(struct seaResponseEntry));
		memcpy(&newEntry->name[0],&tempChar->m_name[0],16);
		// If char is in Moghouse, display prevzone
		newEntry->area=tempChar->getDisplayZone();
		newEntry->country=tempChar->st.n.home;
		newEntry->race=tempChar->l.race;
		newEntry->languages=0x0001;
		unsigned short flags=0x0000;//0x00C0;
		if ((tempChar->h.flags&FLAG_ANON)==FLAG_ANON)
			flags|=0x4000;
		if ((tempChar->h.flags&FLAG_INVITE)==FLAG_INVITE)
		{
			flags|=0x8000;
		}
		if (tempChar->inParty > 0)
		{
			flags|=0x2000; //In Party
			if (tempChar->getParty() != NULL)
			{
				if (tempChar->getParty()->getLeader() == tempChar) //Is the player the party leader
					flags|=0x0008; //Party Leader
			}
		}
		newEntry->flags1=flags;
		newEntry->flags2=flags;
		newEntry->mainjob=tempChar->m_jobMain;
		newEntry->mainjobLevel=tempChar->m_lvlMain;
		newEntry->subjob=tempChar->m_jobSub;
		newEntry->subjobLevel=tempChar->m_lvlSub;
		newEntry->rank=tempChar->st.n.rank[tempChar->st.n.home];
		CLinkshellItem * lsItem;
		lsItem = tempChar->getLinkshellItem();
		if(lsItem != NULL)
			newEntry->linkshellRank = lsItem->getId() - 0x200;

		if(newEntry->mainjob!=0x00) resultList.push_back(newEntry);
	}
	searchList->unlock();

	//#define _FILL_SEARCH_LIST
	#ifdef _FILL_SEARCH_LIST
	resultList.clear();
	for(unsigned int c=(resultList.size()+1);c<=MAX_SEARCH_ENTRIES;c++)
	{
		newEntry=new seaResponseEntry();
		memset(newEntry,0,sizeof(struct seaResponseEntry));
		sprintf(newEntry->name,"%.3d\n",c);
		newEntry->area=0xEB;
		newEntry->country=1;
		newEntry->race=1;
		newEntry->languages=0x0001;
		unsigned short flags=0x4000;//0x00C0;
		newEntry->flags1=flags;
		newEntry->flags2=flags;
		newEntry->mainjob=1;
		newEntry->mainjobLevel=5;
		newEntry->subjob=2;
		newEntry->subjobLevel=2;
		newEntry->rank=1;
		newEntry->linkshellRank = (c%4);
		resultList.push_back(newEntry);
	}
	#endif

	//create search responses, 1 packet per 20 entries
	int results=(int)resultList.size();

	if (results>MAX_SEARCH_ENTRIES)
		results=MAX_SEARCH_ENTRIES;

	int numPackets=(int)(results/20);

	if ((results%20)>0)
		numPackets++;

	if (numPackets==0)
		numPackets=1;

	CSearchResponsePacket* searchRespPacket;

	int numEntries=0;

	int sentBytes=0;
	int receivedBytes=0;
	char buf[8192];

	for(int currPacket=0;currPacket<numPackets;currPacket++)
	{
		numEntries=(int)resultList.size()-20*currPacket;

		if (numEntries>20)
			numEntries=20;

		searchRespPacket=new CSearchResponsePacket();

		searchRespPacket->setNumberOfEntries(numEntries);
		searchRespPacket->setNumberOfPlayersFound((unsigned short)resultList.size());

		for(int currEntry=0;currEntry<numEntries;currEntry++)
		{
			searchRespPacket->setEntry(currEntry,resultList.at(currEntry+20*currPacket));
		}

		searchRespPacket->setKey(key);

		searchRespPacket->setLastPacket(false);
		if ((currPacket+1)==numPackets)
		{
			searchRespPacket->setLastPacket(true);
		}

		//CConsole::outDebOnly("Sending Search Response %d",currPacket);

		sentBytes+=send(s,searchRespPacket->getDataEnciphered(),searchRespPacket->getSize(),0);

		receivedBytes=recv(s,&buf[0],8192,0);
		
		#ifdef _DEBUG
		searchRespPacket->dumpPacket(currPacket);

		if (receivedBytes>0)
		{
			char filename[256];

			unsigned long P[18];
			unsigned long S[4][256];

			unsigned char hash[16];
			//uncrypt
			//read key 1 from packet
			memcpy(key+0x10,buf+receivedBytes-4,4);

			md5((unsigned char*)(key), hash, 20);

			CBlowfish::init((char *)hash,16,P,S[0]);

			unsigned char tmp = (receivedBytes-12)/4;
			tmp -= tmp%2;

			//decipher bytes 8 - (size-4)
			for(unsigned char i = 0; i < tmp; i += 2) {
				CBlowfish::decipher((unsigned long *)buf+2+i, (unsigned long *)buf+i+2+1,P,S[0]);
			}
			//save
			sprintf(filename,"./data/searchResponseResp%.1X.dat",currPacket);
			FILE* p=fopen(filename,"w+");
			if (p)
			{
				fwrite(buf,1,receivedBytes,p);
				fclose(p);
			}
			//use new key from packet
			//memcpy(key+0x14,buf+receivedBytes-0x18,4);

			//instead: reset key
			memcpy(key+0x10,packetData.key1,4);
		}
		//CConsole::outDebOnly("Received %d bytes as response to the search response packet",receivedBytes);
		#endif

		delete searchRespPacket;
	}

	shutdown(s,SD_SEND);

	closesocket(s);

	//delete results

	while(resultList.size()>0)
	{
		delete resultList.front();
		resultList.erase(resultList.begin());
	}

	mysql_thread_end();
	return 1;
}


DWORD WINAPI POLServ(LPVOID lpParam) {

	SOCKET ListenSocket, ClientSocket;




	if(CNetwork::initCommunication(&ListenSocket, IPPROTO_TCP, "51220"))
		return 1;

	while(1){
	    ClientSocket = accept(ListenSocket, NULL, NULL);
	    if (ClientSocket == INVALID_SOCKET) {
	       	closesocket(ListenSocket);
	      	CConsole::outErr("accept failed: %d\n", WSAGetLastError());
	        return 1;
	    }
		CreateThread(0,0,POLComm,&ClientSocket,0,0);

	
	}

	closesocket(ListenSocket);
	return 0;
}



DWORD WINAPI POLComm(LPVOID lpParam){
	SOCKET *s1 = (SOCKET *) lpParam;
	SOCKET ClientSocket = *s1;
	bool newchar = false;
	int iResult, i = 0, iSendResult;
	time_t t;
	char tmp;
	unsigned char recvbuf[1024];
	
		// Receive until the peer shuts down the connection
		do {
			iResult = recv(ClientSocket, (char*)recvbuf, sizeof(recvbuf), 0);
			if (iResult > 0) {
				tmp = recvbuf[4];
				memset(recvbuf, 0, 32);
				switch(i){
					case 0: 
						iResult = 24;
						recvbuf[0] = 0x81;
						t = time(NULL);
						memcpy(recvbuf+0x14, &t, 4);
						break;
					case 1:{
						if(tmp != 0x28) newchar = true;
						recvbuf[0] = 0x28;
						recvbuf[4] = 0x20;
						recvbuf[8] = 1;
						recvbuf[11] = 0x7f;

						
						if(!newchar){
							iResult = 24;
						
						}
						else {
							iResult = 144;
							newchar = false;
						}
					
						break;
					}
				}
				// Echo the buffer back to the sender
				iSendResult = send( ClientSocket,(char*) recvbuf, iResult, 0 );
				if (iSendResult == SOCKET_ERROR) {
					CConsole::outErr("send failed: %d\n", WSAGetLastError());
					break;
				}
			} else  {
				CConsole::outErr("recv failed: %d\n", WSAGetLastError());
				break;
			}
			i++;

			if((i == 3)) {
				i = 0;
				break;
			}

		} while (iResult > 0);

	//		
		// shutdown the connection since we're done
		iResult = shutdown(ClientSocket, SD_SEND);
		if (iResult == SOCKET_ERROR)
			CConsole::outErr("shutdown failed: %d\n", WSAGetLastError());

		// cleanup
		closesocket(ClientSocket);


	
	return 1;
}
